package com.mijie.homi.search.service.index;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.LongField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.index.FieldInvertState;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.similarities.DefaultSimilarity;
import org.apache.lucene.search.similarities.Similarity;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.NIOFSDirectory;
import org.apache.lucene.util.Version;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.wltea.analyzer.lucene.IKAnalyzer;

import com.mijie.core.ApplicationProperties;
import com.mijie.homi.common.domain.SearchResult;
import com.mijie.homi.common.domain.mood.Mood;
import com.mijie.homi.common.domain.user.User;
import com.mijie.homi.common.domain.user.UserExtInfo;
import com.mijie.homi.common.util.HomiUtil;
import com.mijie.homi.search.domain.user.MoodUser;
import com.mijie.homi.search.service.mood.MoodService;
import com.mijie.homi.search.service.user.FriendSerivce;
import com.mijie.homi.search.service.user.UserExtInfoService;
import com.mijie.homi.search.service.user.UserService;
import com.mijie.util.DateUtil;

@Service
public class MoodUserIndexService extends AbstractIndexService {

	@Autowired
	private MoodService moodService;
	@Autowired
	private UserService userService;
	@Autowired
	private UserExtInfoService userExtInfoService;
	@Autowired
	private FriendSerivce friendService;
	
	private static Logger logger = Logger.getLogger(MoodUserIndexService.class);
	private static Version version = Version.LUCENE_43;
	private static Analyzer ikAnalyzer = new IKAnalyzer(true);
	private static String moodUserIndexDir =  ApplicationProperties.get("resource.path")+ApplicationProperties.get("mooduser.index.home");
	
	
	@Override
	public Analyzer getAnalyzer() {
		return ikAnalyzer;
	}

	@Override
	public String getIndexDir() {
		return moodUserIndexDir;
	}

	@Override
	protected void reIndexBefore() {

	}

	static FieldType dontStoreButTokenized = new FieldType();
	static{
		dontStoreButTokenized.setIndexed(true);
		dontStoreButTokenized.setStored(false);
		dontStoreButTokenized.setTokenized(true);
		dontStoreButTokenized.freeze();
	}
	
	private Similarity similarity = new DefaultSimilarity(){

		@Override
		public float tf(float freq) {
			return 1f;
		}

		@Override
		public float idf(long docFreq, long numDocs) {
			return 1f;
		}


		@Override
		public float queryNorm(float sumOfSquaredWeights) {
			return 1f;
		}

		@Override
		public float lengthNorm(FieldInvertState state) {
			return 1f;
		}

		@Override
		public float sloppyFreq(int distance) {
			return 1f;
		}
	};
	
	@Override
	protected void index() throws Exception {
		long s = System.currentTimeMillis();
		Date indexStartAt = new Date();
		IndexWriterConfig config = new IndexWriterConfig(version, getAnalyzer());
		config.setOpenMode(OpenMode.CREATE);
		config.setSimilarity(similarity);
		long currentTimeMillis = System.currentTimeMillis();
		String indexDirpath = getIndexDir()+currentTimeMillis;
		File indexDir = new File(indexDirpath);
		Directory topicDir = new NIOFSDirectory(indexDir);
		IndexWriter indexWriter  = new IndexWriter(topicDir,config);
		logger.info("=========================index mood user begin!!===============================");
		logger.info("mood index file -> "+indexDir.getAbsolutePath());
		int limit = 1000;
		Document doc = null;
		int count = 0;
		int offset = 0;
		StringBuilder token = new StringBuilder();
		int minuteInterval = 0;
		List<MoodUser> list = null;
		HashSet<String> keywords = new HashSet<String>();
		StringBuilder keyword = new StringBuilder();
		Field keyField = null;
		while(true){
			list = userService.listRecentlyLoginMoodUser(DateUtil.getPreDay(7, true),offset, limit);
			if(list.isEmpty()) break;
			offset+=limit;
			for(MoodUser user : list){
				if(user.getLastLoginAt()==null) continue;
				doc = new Document();
				keywords.clear();
				keyword.setLength(0);
				getKeywords(user.getMoodContent(),keywords);
				if(keywords.isEmpty()) continue;
				
				for(String kw : keywords){
					keyword.append(kw).append(" ");
				}
				
				doc.add(new Field("moodContent",keyword.toString(),dontStoreButTokenized));
				doc.add(new LongField("moodId",user.getMoodId(),LongField.TYPE_STORED));
				doc.add(new StringField("mood",String.valueOf(user.getMood()),Store.NO));
				doc.add(new StringField("gender",String.valueOf(user.getGender()),Store.NO));
				doc.add(new StringField("userId",String.valueOf(user.getUserId()),Store.YES));
				doc.add(new LongField("lastLoginAt",user.getLastLoginAt().getTime(),LongField.TYPE_STORED));
				indexWriter.addDocument(doc);
			}
		}
		indexWriter.commit();
		indexWriter.close();
		indexDone(indexDir);
		long e = System.currentTimeMillis();
		logger.info("=========================index mood user done!!===============================");
	}

	public  String getKeywords(String str,HashSet<String> dest) {
		StringReader reader = new StringReader(str); 
	    StringBuilder sb = new StringBuilder();
	    Integer freq = null;
	    String token = null;
	    String[] natures = null;
	    TokenStream ts = null;
	    boolean clearDest = false;
		try {
			ts = getAnalyzer().tokenStream(str, reader);
			CharTermAttribute termAtt = ts.getAttribute(CharTermAttribute.class);
			ts.reset();
			while (ts.incrementToken()){
				token = termAtt.toString();
				if(token.length()>1){
					if(dest==null){
						clearDest = true;
						dest = new HashSet<String>();
					}
					if(dest.add(token))
						sb.append(token).append(" ");
				}
			 }
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}finally{
			if(clearDest)
				dest.clear();
			if(ts!=null){
				try {
					ts.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
		return sb.toString();
	}
	
		
	public SearchResult match(String idxf,int offset, int limit, int userId, int gender) throws Exception{
		Mood mood = moodService.getRecentlyMood(userId);
		if(mood!=null){
			userIdCacheInCurrentThread.set(userId);
			BooleanQuery query = new BooleanQuery();
			query.add(new TermQuery(new Term("userId",String.valueOf(userId))), Occur.MUST_NOT);
			if(gender==1 || gender==2){
				query.add(new TermQuery(new Term("gender",String.valueOf(3-gender))), Occur.MUST_NOT);
			}
			
			HashSet<String> keywords = new HashSet<String>();
			String buff = getKeywords(mood.getContent(), keywords);
			if(keywords.size()>1){
				moodKeywordCacheInCurrentThread.set(keywords);
				for(String key : keywords)
					query.add(new TermQuery(new Term("moodContent",key)), Occur.SHOULD);
			}
			
			Sort sort = new Sort(new SortField("lastLoginAt", SortField.Type.LONG, true));
			
			SearchResult result = search(similarity,idxf, offset, limit, query, sort);
			
			moodKeywordCacheInCurrentThread.remove();
			userIdCacheInCurrentThread.remove();
			return result;
		}
		SearchResult empty = new SearchResult();
		empty.setResult(new JSONArray());
		return null;
	}
	
	private ThreadLocal<HashSet<String>> moodKeywordCacheInCurrentThread = new ThreadLocal<HashSet<String>>(){
		@Override
		protected HashSet<String> initialValue() {
			return null;
		}
	};
	
	private ThreadLocal<Integer> userIdCacheInCurrentThread = new ThreadLocal<Integer>(){
		@Override
		protected Integer initialValue() {
			return null;
		}
	};
	
	
	@Override
	protected Object makeResultItem(String idxf, float socre,Document doc) {
		Set<String> keywords = moodKeywordCacheInCurrentThread.get();
		Mood mood = moodService.getMoodById(Long.parseLong(doc.get("moodId")));
		if(mood==null) return null;
		JSONObject json = new JSONObject();
		User user = userService.getUserById(Integer.parseInt(doc.get("userId")));
		UserExtInfo extInfo = userExtInfoService.getUserExtInfoByUserId(user.getId());
		json.accumulate("userId", user.getId());
		json.accumulate("avatar", HomiUtil.getRealUrl(user.getAvatar()));
		json.accumulate("gender", user.getGender());
		json.accumulate("mood", mood.getMood());
		json.accumulate("distance", 0);
		json.accumulate("nickName", user.getNickName());
		json.accumulate("moodContent", mood.getContent());
		json.accumulate("moodVisibility", mood.getVisibility());
		json.accumulate("lastLoginAt", doc.get("lastLoginAt"));
		//可见度(1所有,2朋友,3私有)
		switch(mood.getVisibility()){
		case 2:
			boolean flag = friendService.isFriend(user.getId(), userIdCacheInCurrentThread.get());
			if(!flag)
				flag = moodService.haveMoodViewPrivilege(userIdCacheInCurrentThread.get(), mood.getId());
			json.accumulate("moodProtected", !flag);
			break;
		case 3:
			boolean haveViewPrivilege = moodService.haveMoodViewPrivilege(userIdCacheInCurrentThread.get(), mood.getId());
			json.accumulate("moodProtected", !haveViewPrivilege);
			break;
		default:
			json.accumulate("moodProtected", false);	
		}
		json.accumulate("match", computeMatchRate(keywords,mood.getContent()));
//		json.accumulate("match", socre*100);
		json.accumulate("idxf", idxf);
		return json;
	}
	
	private float computeMatchRate(Set<String> keywordsOfMyMoodContent,String target){
		float match = 0f;
		float rate = 1f;
		if(keywordsOfMyMoodContent!=null && !keywordsOfMyMoodContent.isEmpty() && 
				target!=null && !target.trim().isEmpty()){
			StringBuilder sb = new StringBuilder();
			HashSet<String> keyword = new HashSet<String>();
			getKeywords(target, keyword);
			int overlap = 0;
			if(!keyword.isEmpty() && !keywordsOfMyMoodContent.isEmpty()){
				if(keywordsOfMyMoodContent.size()>keyword.size()){
					for(String key : keyword){
						if(keywordsOfMyMoodContent.contains(key)){
							overlap++;
							sb.append(key).append(" ");
						}
					}
				}else{
					for(String key : keywordsOfMyMoodContent){
						if(keyword.contains(key)){
							overlap++;
							sb.append(key).append(" ");
						}
					}
				}
				if(overlap>0){
//					match =  (float)keyword.size()/(keyword.size()+keywordsOfMyMoodContent.size()-overlap);
//					rate = (float)overlap/keyword.size();
//					match *= rate ;
					match = (float)overlap/keywordsOfMyMoodContent.size();
					match =  (match * 100 / 100) * 100;
				}
//				logger.info("rate:"+match+",overlap:"+overlap+",target[keyword size:"+keywordsOfMyMoodContent.size()+"]");
			}
		}
		return match;
	}

	@Override
	protected void reIndexDone() {
		
	}

}
